package edu.muohio.cse385.drawsomething;

import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.sql.*;
import javax.imageio.ImageIO;


public class UserInfo {
	private String			_username;
	private String			_email;
	private String			_description;
	private int				_coins;
	private BufferedImage	_picture;
	private boolean			_pictureChanged;
	private int				_visibility;
	
	
	/**
	 * Default constructor. Initializes everything to visibly bad values.
	 */
	private UserInfo() {
		assignInvalidValues();
	}
	
	
	public static UserInfo createUser(
		String username,
		String password,
		String email,
		String description
	) {
		if (username == null || username.isEmpty()) {
			return null;
		}
		else if (getUser(username) != null) {
			return null;
		}
		
		if (password == null || password.isEmpty()) {
			return null;
		}
		
		String createUserCmd = String.format(
			"INSERT INTO user VALUES('%s', '%s', '%s', '%s', 0, null, 2)",
			username,
			password,
			email,
			description
		);
		
		if (DatabaseConnection.executeSingleUpdate(createUserCmd) <= 0) {
			return null;
		}
		
		UserInfo info = new UserInfo();
		info._username = username;
		info._email = email;
		info._description = description;
		info._coins = 0;
		info._picture = null;
		info._pictureChanged = false;
		// smithre5: 2 seems to be a default?
		info._visibility = 2;

		return info;
	}
	
	
	/**
	 * Builds a UserInfo object from data in the database.
	 * 
	 * @param username
	 *		user info to query
	 * @return 
	 *		user info if successful; null otherwise
	 */
	public static UserInfo getUser(String username) {
		if (username == null || username.isEmpty()) {
			return null;
		}
		
		ResultSet rsUserInfo = DatabaseConnection.executeSingleQuery(
			"SELECT * FROM user WHERE username='" + username + "'"
		);
		
		if (rsUserInfo == null) {
			return null;
		}
		
		UserInfo info = new UserInfo();
		
		try {
			if (rsUserInfo.first()) {
				info._username = rsUserInfo.getString("username");
				info._email = rsUserInfo.getString("email");
				info._description = rsUserInfo.getString("description");
				info._coins = rsUserInfo.getInt("coins");
				try {
					if (rsUserInfo.getBlob("pic") != null) {
						info._picture = ImageIO.read(rsUserInfo.getBlob("pic").getBinaryStream());
					}
					else {
						info._picture = null;
					}
				}
				catch (IOException e) {
					System.err.println(
						"ERR UserInfo.getUser: failed to read user picture: " +
						e.getLocalizedMessage()
					);

					info._picture = null;
				}
				info._pictureChanged = false;
				info._visibility = rsUserInfo.getInt("visibility");
			}
			else {
				info = null;
			}
			
			rsUserInfo.close();
		}
		catch (SQLException e) {
			System.err.println(
				"ERR UserInfo.getUser: failed to get user info: " +
				e.getLocalizedMessage()
			);
			
			info = null;
		}
		finally {
			rsUserInfo = null;
		}
		
		DatabaseConnection.endConnection();
		
		return info;
	}
	
	
	/**
	 * Commits data from a UserInfo object into the database.
	 * 
	 * @param info
	 *		object representing current user data
	 * @return 
	 *		true if successful; false otherwise
	 */
	public static boolean updateUser(UserInfo info) {
		if (info == null) {
			return false;
		}
		
		int result = -1;
		String updateInfoCmd = null;
		
		if (info._pictureChanged) {
			updateInfoCmd = String.format(
				"UPDATE user SET email='%s', description='%s', coins=%d, visibility=%d, pic = ? WHERE username='%s'",
				info._email,
				info._description,
				info._coins,
				info._visibility,
				info._username
			);
			
			Connection db = DatabaseConnection.getConnection();
			
			if (db == null) {
				return false;
			}
			
			ByteArrayOutputStream imageOutStream = new ByteArrayOutputStream(
				info._picture.getWidth() * info._picture.getHeight()
			);
			
			try {
				ImageIO.write(info._picture, "png", imageOutStream);
				Blob imageBlob = db.createBlob();
				imageBlob.setBytes(1, imageOutStream.toByteArray());
				
				PreparedStatement updateInfoStmt = db.prepareStatement(updateInfoCmd);
				updateInfoStmt.setBlob(1, imageBlob);
				updateInfoStmt.execute();
				result = updateInfoStmt.getUpdateCount();
				
				imageOutStream.close();
				imageBlob.free();
				updateInfoStmt.close();
				
				info._pictureChanged = false;
			}
			catch (Exception e) {
				System.err.println(
					"ERR UserInfo.updateUser: failed to update user: " + 
					e.getLocalizedMessage()
				);
				
				return false;
			}
			
			DatabaseConnection.endConnection();
		}
		else {
			updateInfoCmd = String.format(
				"UPDATE user SET email='%s', description='%s', coins=%d, visibility=%d WHERE username='%s'",
				info._email,
				info._description,
				info._coins,
				info._visibility,
				info._username
			);
			
			result = DatabaseConnection.executeSingleUpdate(updateInfoCmd);
		}
		
		return result > 0 ? true : false;
	}	
	
	
	private void assignInvalidValues() {
		_username = "INVALID-USER";
		_email = "INVALID-EMAIL";
		_description = "INVALID-DESCRIPTION";
		_coins = -1;
		_picture = null;
		_visibility = -1;
	}
	
	
	public String getName() {
		return _username;
	}
	
	
	public String getEmail() {
		return _email;
	}
	
	
	public void setEmail(String email) {
		// TODO Validate email format.
		_email = email;
	}
	
	
	public String getDescription() {
		return _description;
	}
	
	
	public void setDescription(String description) {
		_description = description;
	}
	
	
	public int getCoins() {
		return _coins;
	}
	

	public void addCoins(int value) {
		// smithre5: This disallows fun stuff like "Bonus coin weekend!" or whatever.
		if (value != 1 && value != 2 && value != 3) {
			return;
		}
		
		_coins += value;
	}
	
	
	public Image getPicture() {
		return _picture;
	}
	
	
	public void setPicture(String picturePath) {
		if (picturePath == null || picturePath.isEmpty()) {
			return;
		}
		
		try {
			_picture = ImageIO.read(new File(picturePath));
			_pictureChanged = true;
		}
		catch (IOException e) {
			System.err.println(
				"ERR UserInfo.setPicture: failed to load picture: " +
				e.getLocalizedMessage()
			);
			
			_pictureChanged = false;
		}
	}
	
	
	public int getVisibility() {
		return _visibility;
	}
	
	
	public void setVisibility(int visibility) {
		_visibility = visibility;
	}
}
